package com.whh.starter.web.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.whh.starter.config.DefaultCodeMapper;
import com.whh.starter.config.RequestContext;
import com.whh.starter.config.SystemException;
import com.whh.starter.config.response.ErrorResponse;
import com.whh.starter.properties.CipherProperties;
import com.whh.starter.utils.json.DesensitizedSerializeByFieldNameFilter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Optional;

/**
 * 打印请求 controller 方法参数和响应消息，
 * 如果需要加密，配置 {@link CipherProperties @CipherProperties}
 * @see CipherProperties
 */
@Aspect
@Component
public class ControllerAspect {
    private static final Logger log = LoggerFactory.getLogger(ControllerAspect.class);

    @SuppressWarnings("unchecked")
    @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public ResponseEntity<Object> joinPint(ProceedingJoinPoint pjp){
        long startTime = System.currentTimeMillis();
        ResponseEntity<Object> response = ResponseEntity.ok().build();
        printParams(pjp);
        //处理异常和映射
        SystemException systemException = null;
        try{
            response = (ResponseEntity<Object>) pjp.proceed();
        } catch (Throwable throwable) {
            ErrorResponse errorResponse;
            log.error("API Catch a Unexpected Error {}",throwable.getMessage(),throwable);
            if(throwable instanceof SystemException){
                systemException = (SystemException) throwable;
                errorResponse = new ErrorResponse()
                        .code(systemException.errorCode())
                        .message(systemException.getMessage())
                        .messageId(RequestContext.getCurrentContext().getCorrelationId());
                response = ResponseEntity.status(systemException.resultCodeMapper().getStatus())
                        .body(errorResponse);
            }else {
                errorResponse = new ErrorResponse().
                        code(HttpStatus.INTERNAL_SERVER_ERROR.value()).
                        message(throwable.getMessage()).
                        messageId(RequestContext.getCurrentContext().getCorrelationId());
                response = ResponseEntity.
                        status(HttpStatus.INTERNAL_SERVER_ERROR).
                        body(errorResponse);
                systemException = new SystemException(DefaultCodeMapper.UNEXPECTED_ERROR);
            }

        } finally {
            RequestContext.getCurrentContext().error(systemException);
            long costTime = System.currentTimeMillis() - startTime;
            Object responseBody = response.getBody();
            if(responseBody == null || responseBody instanceof Resource){
                log.info("ControllerAspect-Response: , CostTime: {}", costTime);
            }
            String responseString = Optional.ofNullable(responseBody).map(item -> JSON.toJSONString(item,
                    new DesensitizedSerializeByFieldNameFilter())).orElse("");
            log.info("ControllerAspect-Response: {}, CostTime: {}", responseString, costTime);
        }
//        Object responseBody = response.getBody();
//        if(responseBody == null || responseBody instanceof Resource){
//            return response;
//        }
//        if(cipherProperties.allowResponseCipher()){
//            String secretKey = RequestContext.getCurrentContext().getSecretKey();
//            //TODO delete
//            if(secretKey == null){
//                secretKey = Base64.getEncoder().encodeToString(SecureUtil.generateKey(SM4.ALGORITHM_NAME,128).getEncoded());
//                log.info("secretKey : {}",secretKey);
//            }
//            String body = Optional.of(responseBody).map(item -> JSON.toJSONString(item,
//                    new DesensitizedSerializeByFieldNameFilter())).orElse("");
//            return ResponseEntity.status(response.getStatusCode()).body(new CipherResponse(cipherService.symmetricEncrypt(body,secretKey)));
//        }
        return response;
    }

    /**
     * 打印参数
     * @param pjp joinPoint
     */
    private void printParams(ProceedingJoinPoint pjp) {
        String methodName = pjp.getSignature().getName();
        Object[] args = pjp.getArgs();
        //组装参数
        StringBuilder params = new StringBuilder();
        for (int i = 0; i < args.length; i++) {
            String p = transString(args[i]);
            if(i == 0){
                params.append(p);
            }else {
                params.append(", ").append(p);
            }
        }
        RequestContext.getCurrentContext().operationName(methodName);
        log.info("params: {}", params);
    }

    /**
     * 参数转String
     * @param arg 参数
     * @return 参数转的String
     */
    private String transString(Object arg) {
        if(arg == null || arg instanceof ServletRequest || arg instanceof ServletResponse ||arg instanceof MultipartFile){
            return null;
        }
        log.info("arg : {}",arg);
        System.out.println("arg.getClass() = " + arg.getClass());
        String p ;
        if(arg instanceof String){
            p = (String) arg;
            try{
                if (p.startsWith("[")) {
                    p = JSON.parseArray(p).toJSONString();
                }else if (p.startsWith("{")){
                    p = JSON.parseObject(p).toJSONString();
                }
            }catch (JSONException e){
                log.info("字符串不符合JSON格式");
            }
        }else {
            p = JSONObject.toJSONString(arg);
        }
        return p;


    }
}
